<template>
  <div class="text-triplet-label">
    <label-header
      :title="detailInfo?.name"
      dmp-label-text="文本三元组"
      :percent="Number(progressInfo?.progress) || 0"
      @update-success="getTaskDetail()"
    />
    <div class="text-triplet-label-main">
      <div class="label-layout-main">
        <div class="main-left">
          <text-list
            ref="textListRef"
            v-model:selected="typeSelected"
            :loading="textListLoading"
            :current-id="currentTextInfo?.id"
            @selected-text="selectedText"
          />
        </div>
        <div class="main-center">
          <title-bar>
            <template #left>
              <div class="title-bar-left">
                <h4>文本详情</h4>
              </div>
            </template>
            <template #right>
              <div class="shortcut-key-wrap">
                <a-button
                  v-if="isAllowOperate && currentTextInfo?.remove !== 1"
                  type="text"
                  class="flex gap-x-4"
                  :disabled="!detailInfo"
                  :loading="delLoading"
                  @click="handleDelete"
                >
                  <shortcut-key :disabled="!detailInfo" :size="24" title="无效数据" keys="W" key-name="W" @trigger="handleDelete" />
                </a-button>

                <a-button
                  v-if="isAllowOperate && currentTextInfo?.remove === 1"
                  type="text"
                  class="flex gap-x-4"
                  :disabled="!detailInfo || textListLoading"
                  :loading="recoverLoading"
                  @click="handleRecover"
                >
                  撤销无效数据
                </a-button>

                <a-button
                  v-if="isAllowOperate"
                  type="primary"
                  class="flex gap-x-4"
                  :disabled="!detailInfo || textListLoading"
                  :loading="saveLoading"
                  @click="handleSave"
                >
                  <shortcut-key
                    :disabled="!detailInfo || textListLoading"
                    :style="{ color: detailInfo ? '#fff' : '' }"
                    :size="24"
                    title="保存"
                    keys="S"
                    key-name="S"
                    @trigger="handleSave"
                  />
                </a-button>
              </div>
            </template>
          </title-bar>
          <div class="main-center-content-wrap">
            <div id="dmp-text-wrap" class="label-text-context my-scrollbar" :class="[!currentTextInfo && 'empty']" v-text="text"></div>
            <div class="dmp-text-triplet-result-wrap">
              <div class="label-result-title">标注结果</div>
              <ul class="label-result my-scrollbar">
                <dmp-relation-result />
              </ul>
            </div>
          </div>
        </div>
        <div class="main-right">
          <label-card
            :task-info="detailInfo"
            type="textTriplet"
            @shortcut-key-trigger="shortcutKeyTrigger"
            @entity-delete="entityDelete"
            @entity-update="entityUpdate"
            @relationDelete="relationDelete"
          />
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { message } from 'ant-design-vue'
  import { uuid } from 'g6-fn'
  import { computed, nextTick, onMounted, onUnmounted, provide, ref } from 'vue'
  import { useRequest } from 'vue-request'
  import { useRoute } from 'vue-router'

  import { textLabelApi } from '@/services/index'

  import LabelCard from '../../components/LabelCard/index.vue'
  import LabelHeader from '../../components/LabelHeader.vue'
  import TitleBar from '../../components/TitleBar.vue'
  import { useProgress, useTask } from '../../hooks/use-task-info'
  import DmpRelationResult from '../components/DmpRelationResult.vue'
  import TextList from '../components/TextList.vue'
  import { useTagChange, useUniqueLabel } from '../hooks/useWebSocket'
  import { useLabelResultListStore } from '../store/index'
  import { CodeEnum, EntityLabelLocationInfo, TextTriadLabel } from '../utils/index'

  const route = useRoute()
  const pageParams = route.params

  const { detailInfo, isEditable, getTaskDetail } = useTask(pageParams?.id as string)
  const { progressInfo, getLabelProgress } = useProgress(pageParams?.id as string)

  // 注入数据
  provide('isEditable', isEditable)

  // 是否可操作文本详情区域
  const isAllowOperate = computed(() => {
    return isEditable.value && currentTextInfo.value && !isLocked.value
  })

  // 标签变更
  const { connect: tagChangeConnect } = useTagChange()
  tagChangeConnect(pageParams.id as string)

  // 标注唯一验证
  const {
    connect: uniqueLabelConnect,
    send: uniqueLabelSend,
    isLocked
  } = useUniqueLabel({
    onOpen: () => {
      getCurrentTextInfo()
    }
  })
  uniqueLabelConnect()

  const labelResultListStore = useLabelResultListStore()

  const textLabel = new TextTriadLabel()

  const textListRef = ref<InstanceType<typeof TextList>>()
  let textLabelWrapDom: HTMLElement | null = null

  // 文本筛选
  const typeSelected = ref<number>(0)

  const textListLoading = ref(false)
  const text = ref('')
  // 文本详情
  const currentTextInfo = ref()
  const selectedText = (params: any) => {
    if (currentTextInfo.value?.id === params.id) {
      return
    }
    getCurrentTextInfo(params.id)
  }

  // 开启标注
  const enableLabel = async (params: any) => {
    textListLoading.value = true
    resetTask()
    await nextTick()
    currentTextInfo.value = params
    text.value = params.text

    textLabel.init(textLabelWrapDom!, { taskId: pageParams.id as string })

    // 监听事件
    textLabel.updateEvent = data => {
      console.log(data)
      if (data.code === CodeEnum.LABEL_HAS_RESULT) {
        message.error(data.message)
      }
    }

    try {
      await getLabelResult(params)
      textLabel.enable = true
      await getLabelProgress()
    } catch (error) {
    } finally {
      textListLoading.value = false
    }
  }

  // 重置任务
  const resetTask = () => {
    text.value = ''
    currentTextInfo.value = null
    labelResultListStore?.reset()
    textLabel.destroy()
  }

  // 获取标注结果
  const getLabelResult = async (params: any) => {
    try {
      const { data } = await textLabelApi.getLabelResult({ taskId: params?.taskId, taskInfoId: params?.id, resultTypeEnum: 2 })
      const entityList: EntityLabelLocationInfo[] = []
      const resultList: any[] = []

      data?.forEach((item: any) => {
        if (item.type === 0) {
          entityList.push({
            labelId: item.taskLabelId,
            labelName: item.labelName,
            start: item.labelStartPos,
            end: item.labelEndPos,
            onlyId: item.resultId,
            labelColor: item.color,
            text: item.labelValue
          })

          return
        }

        resultList.push(item)
      })

      labelResultListStore.remoteToLocalResult(resultList || [])
      nextTick(() => {
        textLabel.renderLabel(entityList)
      })
    } catch (error) {}
  }

  // 撤销
  const { loading: recoverLoading, runAsync: runRecoverText } = useRequest(textLabelApi.recoverText)
  const handleRecover = async () => {
    if (recoverLoading.value || textListLoading.value) {
      return
    }
    await runRecoverText({}, { urlParams: { id: currentTextInfo?.value?.id }, isAddUid: true })
    getCurrentTextInfo()
    textListRef.value?.refreshList()
    message.success('撤销成功')
  }

  // 删除文本
  const { loading: delLoading, runAsync: runDelText } = useRequest(textLabelApi.delText)
  const handleDelete = async () => {
    if (delLoading.value || textListLoading.value) {
      return
    }
    await runDelText({}, { urlParams: { id: currentTextInfo?.value?.id }, isAddUid: true })
    getCurrentTextInfo()
    textListRef.value?.refreshList()
    message.success('操作成功')
  }

  // 保存
  const { loading: saveLoading, runAsync: runSave } = useRequest<any>(textLabelApi.save)
  const handleSave = async () => {
    if (saveLoading.value || textListLoading.value) {
      return
    }
    const labelLocationInfo = textLabel.getLabelLocationInfo()
    const entityList: any[] = []
    const resultList: any[] = []
    labelLocationInfo.forEach(item => {
      entityList.push({
        type: 0,
        resultId: item.onlyId,
        taskLabelId: item.labelId,
        taskLabelName: item.labelName,
        labelValue: item.text,
        labelStartPos: item.start,
        labelEndPos: item.end
      })
    })

    labelResultListStore?.relationResultList.forEach(item => {
      const { relationLabel, startLabel, endLabel } = item
      resultList.push({
        type: 1,
        taskLabelId: relationLabel.id,
        taskLabelName: relationLabel.name,
        beginLabelResultId: startLabel.onlyId,
        endLabelResultId: endLabel.onlyId
      })
    })

    const { data } = await runSave(
      {
        taskId: detailInfo?.value?.id,
        taskInfoId: currentTextInfo?.value?.id,
        //  0-"其他文本" 1-"命名实体" 2-"三元组"
        resultTypeEnum: 2,
        resultReqVOList: [...entityList, ...resultList]
      },
      {
        isAddUid: true
      }
    )
    message.success('保存成功')
    if (!data) {
      getLabelProgress()
      return
    }
    textListRef.value?.refreshList()
    getCurrentTextInfo()
  }

  // 标签快捷键触发
  const shortcutKeyTrigger = async (data: any) => {
    // 替换标签
    if (textLabel.currentLabelWrap) {
      await textLabel.replaceTag(textLabel.currentLabelWrap, {
        id: data.id,
        title: data.name,
        color: data.color
      })
      textLabel.currentLabelComponent?.unmount()
      textLabel.currentLabelWrap?.remove()
      textLabel.currentLabelWrap = null
      textLabel.currentRange = null
      return
    }
    if (!textLabel.currentRange) {
      return
    }
    // 创建标签
    textLabel.insertTagByRange(textLabel.currentRange, {
      id: data.id,
      title: data.name,
      color: data.color,
      onlyId: uuid()
    })
    textLabel.dropdownComponent?.unmount()
    textLabel.currentRange = null
  }

  // 实体标签被删除
  const entityDelete = (data: any) => {
    const labelList = textLabelWrapDom?.querySelectorAll('.label-text-tag-wrap')
    labelList?.forEach((item: any) => {
      if (item.getAttribute('label-id') === data.id) {
        textLabel.removeTag(item, { force: true })
      }
    })
    textLabel?.removeAllRanges()
    textListRef.value?.refreshList()
    getCurrentTextInfo()
  }

  // 关系标签被删除
  const relationDelete = () => {
    textListRef.value?.refreshList()
    getCurrentTextInfo()
  }

  // 标签被更新
  const entityUpdate = (data: any) => {
    const labelList = textLabelWrapDom?.querySelectorAll('.label-text-tag-wrap')
    labelList?.forEach((item: any) => {
      if (item.getAttribute('label-id') === data.id) {
        textLabel.replaceTag(
          item,
          {
            id: data.id,
            title: data.name,
            color: data.color
          },
          { force: true }
        )
      }
    })
  }

  // 获取当前标注任务文本
  const getCurrentTextInfo = async (taskInfoId?: string) => {
    textLabel.enable = false
    const { data } = await textLabelApi.getCurrentTextInfo({
      taskId: pageParams?.id,
      taskInfoId,
      markStatusType: typeSelected.value
    })

    if (!data?.id) {
      uniqueLabelSend({
        lastInfoId: currentTextInfo.value?.id,
        currentInfoId: undefined,
        taskId: pageParams?.id
      })
      text.value = '不存在可标注的数据'
      currentTextInfo.value = null
      labelResultListStore?.reset()
      textLabel.destroy()
      getLabelProgress()
      return
    }
    uniqueLabelSend({
      lastInfoId: currentTextInfo.value?.id,
      currentInfoId: data?.id,
      taskId: pageParams?.id
    })
    enableLabel(data)
  }

  onMounted(() => {
    textLabelWrapDom = document.getElementById('dmp-text-wrap') as HTMLElement
    getTaskDetail()
  })

  onUnmounted(() => {
    resetTask()
  })
</script>

<style lang="less" scoped>
  @import '../../styles/index.less';

  .text-triplet-label {
    width: 100%;
    height: 100%;
    background: #fff;

    .text-triplet-label-main {
      height: calc(100% - 54px);
    }
  }

  .label-layout-main {
    display: flex;
    width: 100%;
    height: 100%;

    .main-left {
      display: flex;
      width: 320px;
      height: 100%;
      border-right: 1px solid @mask1;
      flex-direction: column;
      flex-shrink: 0;
    }

    .main-center {
      flex: 1;
      height: 100%;
      border-right: 1px solid @mask1;

      .title-bar-left {
        display: flex;
        align-items: center;
        gap: 0 12px;

        > h4 {
          font-size: 14px;
          white-space: nowrap;
          color: @text02;
          font-weight: 700;
        }
      }

      .main-center-content-wrap {
        display: flex;
        width: 100%;
        height: calc(100% - 56px);
        border-top: 1px solid @mask1;

        .label-text-context {
          overflow-y: auto;
          padding: 20px 24px;
          width: 100%;
          height: 100%;
          font-size: 14px;
          color: @text01;
          background-color: #f5f5f5;
          font-weight: 400;
        }

        .dmp-text-triplet-result-wrap {
          display: flex;
          width: 256px;
          height: 100%;
          min-height: 0;
          flex-direction: column;
          flex-shrink: 0;

          .label-result-title {
            padding: 12px;
            font-size: 14px;
            color: @text02;
            font-weight: 700;
            line-height: 22px;
          }

          .label-result {
            overflow-y: auto;
            padding: 0 12px 12px;
            width: 100%;
            flex: 1;

            > li {
              margin-bottom: 8px;

              &:last-child {
                margin-bottom: 0;
              }
            }
          }
        }
      }
    }

    .main-right {
      width: 320px;
      height: 100%;
      flex-shrink: 0;
    }
  }
</style>
